package com.bwie.service.impl;

import com.alibaba.fastjson.JSON;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.bwie.config.OSSFileUtil;
import com.bwie.mapper.GoodsMapper;
import com.bwie.mapper.TbOrderMapper;
import com.bwie.mapper.TypeMapper;
import com.bwie.pojo.TbGoods;
import com.bwie.pojo.TbOrder;
import com.bwie.pojo.TbType;
import com.bwie.pojo.TbUser;
import com.bwie.service.FeignService;
import com.bwie.service.GoodsService;
import com.bwie.utils.R;
import com.bwie.vo.EsGoods;
import com.bwie.vo.LoginVo;
import com.bwie.vo.MessageVo;
import com.bwie.vo.PageVo;
import com.rabbitmq.client.Channel;
import io.jsonwebtoken.Claims;
import io.jsonwebtoken.Jwts;
import jodd.util.StringUtil;
import org.checkerframework.checker.units.qual.A;
import org.elasticsearch.index.query.BoolQueryBuilder;
import org.elasticsearch.index.query.QueryBuilders;
import org.elasticsearch.search.fetch.subphase.highlight.HighlightBuilder;
import org.springframework.amqp.core.Message;
import org.springframework.amqp.rabbit.annotation.RabbitListener;
import org.springframework.amqp.rabbit.connection.CorrelationData;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.Ordered;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.elasticsearch.core.ElasticsearchRestTemplate;
import org.springframework.data.elasticsearch.core.SearchHit;
import org.springframework.data.elasticsearch.core.SearchHits;
import org.springframework.data.elasticsearch.core.query.NativeSearchQueryBuilder;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestHeader;
import org.springframework.web.multipart.MultipartFile;

import javax.annotation.PostConstruct;
import java.io.IOException;
import java.util.*;

/**
 * @Title:
 * @BelongProjecet zk3
 * @BelongPackage com.bwie.service.impl
 * @Description:
 * @Copyright 研发一部
 * @Author: Mr.yan
 * @Date: 2024/6/12 9:36
 * @Version V1.0
 */
@Service
public class GoodsServiceImpl implements GoodsService {
    @Autowired
    private GoodsMapper goodsMapper;
    @Autowired
    private OSSFileUtil ossFileUtil;
    @Autowired
    private StringRedisTemplate stringRedisTemplate;
    @Autowired
    private ElasticsearchRestTemplate elasticsearchRestTemplate;
    @Autowired
    private TypeMapper typeMapper;
    @Autowired
    private RabbitTemplate rabbitTemplate;
    @Autowired
    private TbOrderMapper tbOrderMapper;
    @Autowired
    private FeignService feignService;

    @PostConstruct
    public void  rabbitInit(){
        System.out.println("本大爷开始启动了");
        //消息发送失败重新发送一次
        rabbitTemplate.setReturnCallback(new RabbitTemplate.ReturnCallback() {
            @Override
            public void returnedMessage(Message message, int i, String s, String s1, String s2) {
                String s3 = new String(message.getBody());
                rabbitTemplate.convertSendAndReceive(s1,s2,s3);
            }
        });
        //判断消息是否发送成功
        rabbitTemplate.setConfirmCallback(new RabbitTemplate.ConfirmCallback() {
            @Override
            public void confirm(CorrelationData correlationData, boolean b, String s) {
                if (b){
                    System.out.println("消息发送成功");
                }else {
                    System.out.println("消息发送失败");
                }
            }
        });
    }


    @Override
    public R uploadFile(MultipartFile file) {
        try {
            String url = ossFileUtil.uploadFile(file);
            return R.success(url);
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
    }

    @Override
    public R uploadVideo(MultipartFile file) {
        try {
            String url = ossFileUtil.uploadFileByBreakingPoint(file);
            return R.success(url);
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
    }

    @Override
    public R goodsList(PageVo pageVo) {
        //1.分页
        int pageNum = 0;
        if (pageVo.getPageNum()>0){
            pageNum = pageVo.getPageNum() - 1;
        }
        PageRequest pageRequest = PageRequest.of(pageNum, pageVo.getPageSize());

        //2.高亮
        HighlightBuilder highlightBuilder = new HighlightBuilder();
        highlightBuilder.preTags("<font color='red'>").postTags("</font>").field("goodsName");

        //3.模糊查询
        BoolQueryBuilder boolQueryBuilder = new BoolQueryBuilder();
        if (!StringUtil.isEmpty(pageVo.getGoodsName())){
            boolQueryBuilder.must(
                    QueryBuilders.matchQuery("goodsName",pageVo.getGoodsName())
            );
        }

        //4.类型查询
        if (pageVo.getTypeId()!= null){
            boolQueryBuilder.must(
                    QueryBuilders.matchQuery("typeId",pageVo.getTypeId())
            );
        }

        //5.创建
        NativeSearchQueryBuilder nativeSearchQueryBuilder = new NativeSearchQueryBuilder();
        nativeSearchQueryBuilder.withQuery(boolQueryBuilder)
                .withPageable(pageRequest)
                .withHighlightBuilder(highlightBuilder);

        //6.查询数据
        SearchHits<EsGoods> search = elasticsearchRestTemplate.search(nativeSearchQueryBuilder.build(), EsGoods.class);

        //7.获取总数据
        long total = search.getTotalHits();

        //8.将数据放入集合
        ArrayList<EsGoods> esGoods = new ArrayList<>();
        for (SearchHit<EsGoods> esGoodsSearchHit : search) {
            EsGoods content = esGoodsSearchHit.getContent();

            List<String> goodsName = esGoodsSearchHit.getHighlightField("goodsName");
            if (goodsName!=null && goodsName.size()>0){
                String goodsNames = goodsName.get(0);
                content.setGoodsName(goodsNames);
            }
            esGoods.add(content);
        }

        HashMap<String, Object> map = new HashMap<>();
        map.put("pageNum",pageNum);
        map.put("total",total);
        map.put("pageSize",pageVo.getPageSize());
        map.put("records",esGoods);

        return R.success(map);
    }

    @Override
    @Transactional
    public R addGoods(TbGoods tbGoods) {
        try {
            //1.先添加数据库
            goodsMapper.insert(tbGoods);
            //2.通过类型id找到类型名称
            TbType type = typeMapper.selectOne(
                    new QueryWrapper<TbType>().lambda().eq(TbType::getId, tbGoods.getTypeId())
            );
            //3.同步到es中
            EsGoods esGoods = new EsGoods();
            BeanUtils.copyProperties(tbGoods,esGoods);
            esGoods.setTypeName(type.getTypeName());
            elasticsearchRestTemplate.save(esGoods);
        } catch (BeansException e) {
            throw new RuntimeException(e);
        }

        return R.success();
    }

    @Override
    public R addOrder(String token, Integer id) {
        //解析token
        Claims claims = Jwts.parser().setSigningKey("bwie").parseClaimsJws(token).getBody();
        Integer userId = claims.get("id", Integer.class);
        //1.现在的时间
        Date date = new Date();
        long time = date.getTime();//现在的时间
        //2.通过商品id拿到抢购的有效时间
        TbGoods tbGoods = goodsMapper.selectOne(
                new QueryWrapper<TbGoods>().lambda().eq(TbGoods::getId, id)
        );
        if (time<tbGoods.getStartTime().getTime() || time>tbGoods.getEndTime().getTime()){
            return R.error(500,"抢购活动已失效");
        }
        //库存减一
        if (tbGoods.getGoodsNum()<0){
            return R.error(500,"已抢完不");
        }
        tbGoods.setGoodsNum(tbGoods.getGoodsNum()-1);
        //通过类型id查询赋值
        TbType tbType = typeMapper.selectOne(
                new QueryWrapper<TbType>().lambda().eq(TbType::getId, tbGoods.getTypeId())
        );
        tbGoods.setTypeName(tbType.getTypeName());
        goodsMapper.updateById(tbGoods);
        //同步es
        EsGoods esGoods = new EsGoods();
        BeanUtils.copyProperties(tbGoods,esGoods);
        elasticsearchRestTemplate.save(esGoods);
        //3.添加抢购信息
        TbOrder tbOrder = new TbOrder();
        tbOrder.setUserId(userId);
        tbOrder.setGoodsId(id);
        tbOrder.setOrderTime(date);

        //4.通过mq添加订单信息
        MessageVo messageVo = new MessageVo();
        String messageId = UUID.randomUUID().toString();
        messageVo.setMessageId(messageId);

        //将要添加的信息存入vo中
        messageVo.setMessageBody(JSON.toJSONString(tbOrder));

        //将vo信息存入缓存中，为了防止重复消费
        String message = JSON.toJSONString(messageVo);
        stringRedisTemplate.opsForValue().set(messageId,message);

        //发送
        rabbitTemplate.convertSendAndReceive("directExchange","orderBind",message);

        return R.success("抢购成功");
    }

    @Override
    public R orderList(String token) {
        //解析token
        Claims claims = Jwts.parser().setSigningKey("bwie").parseClaimsJws(token).getBody();
        Integer userId = claims.get("id", Integer.class);
        //查询
        List<TbOrder> orderList = tbOrderMapper.orderList(userId);
        return R.success(orderList);
    }

    @Override
    public R typeList() {
        List<TbType> typeList = goodsMapper.typeList();
        return R.success(typeList);
    }

    @Override
    public R feignLogin(LoginVo loginVo) {
        R r = feignService.userLogin(loginVo);
        if (r.getCode()!=200){
            return r;
        }
        String s = JSON.toJSONString(r.getData());
        TbUser tbUser = JSON.parseObject(s, TbUser.class);

        return R.success(tbUser);
    }

    @Scheduled(cron = "0 0/1 * * * ?")
    public void goodsData(){
        List<EsGoods> goodsList = goodsMapper.goodsList();
        elasticsearchRestTemplate.save(goodsList);
    }

    @RabbitListener(queues = "orderQueue")
    public void addOrderData(Message message, Channel channel){
        try {
            //1.接受发送过来的参数
            String s = new String(message.getBody());
            MessageVo messageVo = JSON.parseObject(s, MessageVo.class);
            String messageId = messageVo.getMessageId();
            //2.判断缓存是否有，为了防止重复消费
            if (!stringRedisTemplate.hasKey(messageId)){
                System.out.println("消息已消费");
                channel.basicAck(message.getMessageProperties().getDeliveryTag(),true);
                return;
            }
            //3.添加订单信息
            TbOrder tbOrder = JSON.parseObject(messageVo.getMessageBody(), TbOrder.class);
            tbOrderMapper.insert(tbOrder);

            //4.消费
            channel.basicAck(message.getMessageProperties().getDeliveryTag(),true);

            //5.删除缓存
            stringRedisTemplate.delete(messageId);
            System.out.println("消费成功");
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
    }

}
